﻿using OA.Infrastructure.Extensions;

namespace AMS.Services.Impl;

public class FixedAssetsStorageService : IFixedAssetsStorageService
{
    private readonly DbContext _context;

    private readonly IApprovalService _approvalService;

    public FixedAssetsStorageService(DbContext context, IApprovalService approvalService)
    {
        _context = context;
        _approvalService = approvalService;
    }

    public async Task<PageResult<FixedAssetsStorage.QueryModel>> GetPageAsync(FixedAssetsStorage.ConditionSelectorCondition condition, CancellationToken cancellationToken)
    {
        var result = new PageResult<FixedAssetsStorage.QueryModel>();

        try
        {
            var expression = GetConditionSelectorCondition(condition);

            var data = await _context.Set<FixedAssetsStorage>()
                .Include(x => x.Category)
                .Include(x => x.Supplier)
                .Include(m => m.Commiter).ThenInclude(x => x.DirectLeader).ThenInclude(x => x.Roles)
                .Include(m => m.Commiter).ThenInclude(x => x.Department).ThenInclude(x => x.Roles)
                .Include(m => m.Commiter).ThenInclude(x => x.Roles)
                .Where(expression)
                .OrderByDescending(x => x.CreatedDate)
                .Skip((condition.PageNum - 1) * condition.PageSize)
                .Take(condition.PageSize)
                .AsNoTracking()
                .ToListAsync(cancellationToken);

            result.Data = data.Select(x => GetQueryModel(x));

            result.PageNum = condition.PageNum;

            result.PageSize = condition.PageSize;

            result.Total = await _context.Set<FixedAssetsStorage>().CountAsync(expression, cancellationToken);

            result.Status = OperationalResult.SUCCESS;
        }
        catch (Exception error)
        {
            result.Status = OperationalResult.ERROR;

            result.Message = error.ToString();
        }

        return result;
    }

    public async Task<OperationalResult> CreateAsync(FixedAssetsStorage.CreateModel fixedAssetsStorage, CancellationToken cancellationToken)
    {
        var result = new OperationalResult();

        var currentUser = _context.CurrentUser();

        try
        {
            var entity = new FixedAssetsStorage
            {
                Name = fixedAssetsStorage.Name,
                Specs = fixedAssetsStorage.Specs,
                Univalent = fixedAssetsStorage.Univalent,
                StorageLocation = fixedAssetsStorage.StorageLocation,
                PurchaseDate = fixedAssetsStorage.PurchaseDate,
                Description = fixedAssetsStorage.Description,
                Remark = fixedAssetsStorage.Remark,
                Commiter = currentUser
            };

            var entityCategory = await _context.Set<AssetsCategory>().FindAsync(new object[] { fixedAssetsStorage.CategoryId }, cancellationToken);

            if (entityCategory is not null)
            {
                entity.Category = entityCategory;
            }

            var entitySupplier = await _context.Set<Supplier>().FindAsync(new object[] { fixedAssetsStorage.SupplierId }, cancellationToken);

            if (entitySupplier is not null)
            {
                entity.Supplier = entitySupplier;
            }

            await _context.Set<FixedAssetsStorage>().AddAsync(entity, cancellationToken);

            var affectedRows = await _context.SaveChangesAsync(cancellationToken);

            result.Status = affectedRows > 0 ? OperationalResult.SUCCESS : OperationalResult.FAILURE;

            result.Message = $"{affectedRows} rows affected.";

            if (result.Status == OperationalResult.SUCCESS)
            {
                _ = await _approvalService.CreateApprovalAsync(currentUser, entity.Id, ApprovalType.Storage, AssetsType.Durable, fixedAssetsStorage.Remark, ApprovalService.fixedAssetsStorageApprovalFlow, cancellationToken);
            }
        }
        catch (Exception error)
        {
            result.Status = OperationalResult.ERROR;

            result.Message = error.ToString();
        }

        return result;
    }

    private static Expression<Func<FixedAssetsStorage, bool>> GetConditionSelectorCondition(FixedAssetsStorage.ConditionSelectorCondition condition)
    {
        Expression<Func<FixedAssetsStorage, bool>>? expression = null;

        if (condition.Keywords is not null)
        {
            Expression<Func<FixedAssetsStorage, bool>>? keywords = null;
            keywords = fixedAssetsStorage => fixedAssetsStorage.Name.Contains(condition.Keywords);
            keywords = keywords.Or(fixedAssetsStorage => fixedAssetsStorage.Specs.Contains(condition.Keywords));
            keywords = keywords.Or(fixedAssetsStorage => fixedAssetsStorage.Supplier.Name.Contains(condition.Keywords));
            keywords = keywords.Or(fixedAssetsStorage => fixedAssetsStorage.Commiter.JobNumber.Contains(condition.Keywords));
            keywords = keywords.Or(fixedAssetsStorage => fixedAssetsStorage.Commiter.Name.Contains(condition.Keywords));
            keywords = keywords.Or(fixedAssetsStorage => fixedAssetsStorage.Commiter.Department.Name.Contains(condition.Keywords));
            keywords = keywords.Or(fixedAssetsStorage => fixedAssetsStorage.StorageLocation.Contains(condition.Keywords));
            expression = expression?.And(keywords) ?? keywords;
        }

        if (condition.StatusSelector is not null)
        {
            Expression<Func<FixedAssetsStorage, bool>>? statusSelector = null;
            statusSelector = fixedAssetsStorage => condition.StatusSelector == fixedAssetsStorage.Status;
            expression = expression?.And(statusSelector) ?? statusSelector;
        }

        if (condition.SupplierSelectorId is not null)
        {
            Expression<Func<FixedAssetsStorage, bool>>? supplierSelector = null;
            supplierSelector = fixedAssetsStorage => condition.SupplierSelectorId == fixedAssetsStorage.Supplier.Id;
            expression = expression?.And(supplierSelector) ?? supplierSelector;
        }

        if (condition.CategorySelectorId is not null)
        {
            Expression<Func<FixedAssetsStorage, bool>>? categorySelector = null;
            categorySelector = fixedAssetsStorage => condition.CategorySelectorId == fixedAssetsStorage.Category.Id;
            expression = expression?.And(categorySelector) ?? categorySelector;
        }

        return expression ?? (fixedAssetsStorage => true);
    }

    private static FixedAssetsStorage.QueryModel GetQueryModel(FixedAssetsStorage fixedAssetsStorage)
    {
        return new FixedAssetsStorage.QueryModel
        {
            Id = fixedAssetsStorage.Id,
            CreatedDate = fixedAssetsStorage.CreatedDate,
            LastModifiedDate = fixedAssetsStorage.LastModifiedDate,
            Name = fixedAssetsStorage.Name,
            Category = AssetsCategoryService.GetQueryModel(fixedAssetsStorage.Category),
            Specs = fixedAssetsStorage.Specs,
            Univalent = fixedAssetsStorage.Univalent,
            Supplier = SupplierService.GetQueryModel(fixedAssetsStorage.Supplier),
            StorageLocation = fixedAssetsStorage.StorageLocation,
            PurchaseDate = fixedAssetsStorage.PurchaseDate,
            Description = fixedAssetsStorage.Description,
            Remark = fixedAssetsStorage.Remark,
            Commiter = DefinitionServiceExtensions.GetUserQueryModel(fixedAssetsStorage.Commiter),
            Status = fixedAssetsStorage.Status,
        };
    }
}
